package com.haidechizi.i18n.common.execute.strategy;

import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import com.haidechizi.i18n.common.annotation.I18nField;
import com.haidechizi.i18n.common.annotation.I18nModel;
import com.haidechizi.i18n.common.execute.AbstractTranslateExecute;
import com.haidechizi.i18n.common.support.I18nReflectUtils;
import com.haidechizi.i18n.common.translate.Translator;
import com.haidechizi.i18n.common.utils.GlobalUtils;
import com.haidechizi.i18n.common.utils.NamedThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author dejun.wang
 * @date 2021/12/14
 */
@Slf4j
public class MultiThreadTranslateExecute extends AbstractTranslateExecute {

    private final int core = Runtime.getRuntime().availableProcessors();

    // 自定义线程池，超线程技术，因此，核心线程数为CPU*2，采用CallerRunsPolicy不丢弃任务
    private ThreadPoolExecutor executor = new ThreadPoolExecutor(core * 2, core * 2,
            60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100),
            new NamedThreadFactory("I18nFilterForMultiThread"), new ThreadPoolExecutor.CallerRunsPolicy());

    // 自定义线程池，超线程技术，因此，核心线程数为CPU*2，采用CallerRunsPolicy不丢弃任务
    private ThreadPoolExecutor fieldExecutor = new ThreadPoolExecutor(core * 2, core * 2,
            60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100),
            new NamedThreadFactory("I18nFilterForMultiThread-field"), new ThreadPoolExecutor.CallerRunsPolicy());

    @Override
    public void processI18nFields(List<Object> models) {
        if (CollectionUtils.isEmpty(models)) {
            return;
        }
        Object object = models.get(0);
        if (object == null) {
            log.warn("list中出现空对象: {}, {}", models);
        }
        Class<?> clazz = object.getClass();
        I18nModel i18nAnnotation = I18nReflectUtils.getI18nModelAnnotation(clazz);
        if (i18nAnnotation == null) {
            return;
        }

        //简单属性处理
        processSimpleField(models, i18nAnnotation, I18nReflectUtils.getSimpleFieldsListWithAnnotation(clazz));

        //如果I18nField里面有完备的信息，说明他在一个组合的DTO里面，不属于单独的一个域对象，单独使用自己的元信息查询
        processCombinedField(models, I18nReflectUtils.getCombinedFieldsListWithAnnotation(clazz));

        //定制逻辑的field处理
        processCustomField(models, I18nReflectUtils.getCustomFieldsListWithAnnotation(clazz));

        //将不属于需要国际化的字段提取出来，再次递归走assembleI18nInfo的流程，处理嵌套的需要国际化的对象
        models.forEach(model -> {
            I18nReflectUtils.getOtherFieldsListWithAnnotation(clazz).forEach(el -> {
                try {
                    assembleI18nInfo(FieldUtils.readField(model, el.getName(), true));
                } catch (IllegalAccessException e) {
                    log.error("assembleI18nInfo", e);
                }
            });
        });

    }

    private void processCombinedField(List<Object> models, List<Field> combinedFields) {
        if (CollectionUtils.isEmpty(combinedFields)) {
            return;
        }
        String languageName = GlobalUtils.getLanguage();
        CountDownLatch fieldCountDownLatch = new CountDownLatch(combinedFields.size());
        combinedFields.forEach(combinedField -> {
            fieldExecutor.submit(() -> {
                try {
                    I18nField i18nField = I18nReflectUtils.getI18nFieldAnnotation(combinedField);
                    String systemCode = i18nField.systemCode();
                    String businessCode = i18nField.businessCode();
                    String businessKeyName = i18nField.businessKey();
                    String fieldMappingInPlatform = i18nField.value();
                    if (StringUtils.isBlank(fieldMappingInPlatform)) {
                        log.error("I18nField 注解未设置value属性,业务Code：{}, ", businessCode);
                        return;
                    }
                    List<String> businessKeyListOfTheCombinedField = extractBusinessKeyList((List) models,
                            businessKeyName);

                    Map<String, Map<String, String>> idFieldsMapOfCombinedField =
                            i18nInfoUtils.queryLocaleInfo(systemCode,
                            businessCode, languageName, businessKeyListOfTheCombinedField);
                    if (idFieldsMapOfCombinedField.isEmpty()) {
                        log.debug("国际化平台无相关国际化数据，跳过循环,系统Code：{}, 业务code：{}, 语言：{}, 业务主键:{},国际化结果：{}",
                                systemCode, businessCode, languageName, businessKeyListOfTheCombinedField,
                                idFieldsMapOfCombinedField);
                        return;
                    }
                    CountDownLatch countDownLatch = new CountDownLatch(models.size());
                    models.stream().forEach(model -> {
                        executor.execute(() -> {
                            try {
                                Object businessKeyValue = FieldUtils.readField(model, businessKeyName, true);
                                if (businessKeyValue != null) {
                                    String businessKey = String.valueOf(businessKeyValue);
                                    Map<String, String> fieldsMap = idFieldsMapOfCombinedField.get(businessKey);
                                    if (fieldsMap == null || fieldsMap.isEmpty()) {
                                        log.debug("国际化平台无相关国际化数据，跳过循环,系统Code：{}, 业务code：{}, 语言：{}, 业务主键:{},国际化结果：{}",
                                                systemCode, businessCode, languageName, businessKey, fieldsMap);
                                        return;
                                    }
                                    String substitution = fieldsMap.get(fieldMappingInPlatform);
                                    if (StringUtils.isNotEmpty(substitution)) {
                                        log.debug("====processCombinedField====>参数名字：{},========>值：{}",
                                                combinedField.getName(), substitution);
                                        FieldUtils.writeField(combinedField, model, substitution, true);
                                    } else {
                                        log.debug("国际化值为空，跳过替换字段：{},主键：{},替换值：{}", combinedField.getName(),
                                                businessKey, substitution);
                                    }
                                }
                            } catch (Exception e) {
                                log.error("processCombinedField error:", e);
                            } finally {
                                countDownLatch.countDown();
                            }
                        });

                    });
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        log.error("processCombinedField countDownLatch error", e);
                    }
                } finally {
                    fieldCountDownLatch.countDown();
                }
            });

        });
        try {
            fieldCountDownLatch.await();
        } catch (InterruptedException e) {
            log.error("processCombinedField countDownLatch error", e);
        }
    }

    private void processCustomField(List<Object> models, List<Field> customFields) {
        if (CollectionUtils.isEmpty(customFields)) {
            return;
        }
        String language = GlobalUtils.getLanguage();
        CountDownLatch countDownLatch = new CountDownLatch(customFields.size());
        customFields.forEach(customField -> {
            fieldExecutor.execute(() -> {
                try {
                    I18nField i18nField = customField.getAnnotation(I18nField.class);
                    String metaCalculator = i18nField.metaCalculator();
                    Translator calculator = i18nInfoUtils.getTranslator(metaCalculator);
                    if (calculator == null) {
                        log.error("自定义处理器：{} ,不存在，请查看代码", metaCalculator);
                        return;
                    }
                    // 多线程传递ThreadLocal变量
                    GlobalUtils.setLanguage(language);
                    calculator.translate(models);
                    // 清除线程池中的ThreadLocal变量，防止内存泄漏以及其他异常
                    GlobalUtils.clear();

                } finally {
                    countDownLatch.countDown();
                }
            });
        });
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            log.error("processCustomField countDownLatch error", e);
        }
    }

    private void processSimpleField(List<Object> models, I18nModel i18nAnnotation, List<Field> simpleFields) {
        if (CollectionUtils.isEmpty(simpleFields)) {
            return;
        }
        //locale是dubbo调用时，被调用方传入的
        String languageName = GlobalUtils.getLanguage();
        String systemCodeModel = i18nAnnotation.systemCode();
        String businessCodeModel = i18nAnnotation.businessCode();
        String businessKeyNameModel = i18nAnnotation.businessKey();

        Stopwatch stopwatch = Stopwatch.createStarted();

        List<String> businessKeyList = extractBusinessKeyList(models, businessKeyNameModel);
        Map<String, Map<String, String>> idFieldsMapModel = i18nInfoUtils.queryLocaleInfo(systemCodeModel,
                businessCodeModel, languageName, businessKeyList);
        log.debug("========国际化语言返回结果耗時:========> " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + "ms");
        if (idFieldsMapModel.isEmpty()) {
            log.debug("国际化平台无相关国际化数据，跳过循环,系统Code：{}, 业务code：{}, 语言：{}, 业务主键:{},国际化结果：{}",
                    systemCodeModel, businessCodeModel, languageName, businessKeyList, idFieldsMapModel);
            return;
        }
        // 使用线程并行处理，但是非异步
        CountDownLatch modelCountDownLatch = new CountDownLatch(models.size());

        models.forEach(model -> {
            executor.execute(() -> {
                try {
                    if (Objects.isNull(model)) {
                        return;
                    }
                    String id = String.valueOf(FieldUtils.readField(model, businessKeyNameModel, true));
                    Map<String, String> fieldMap = idFieldsMapModel.get(id);
                    if (fieldMap == null || fieldMap.isEmpty()) {
                        log.debug("国际化平台无相关国际化数据，跳过循环,系统Code：{}, 业务code：{}, 语言：{}, 业务主键:{},国际化结果：{}",
                                systemCodeModel, businessCodeModel, languageName, id, fieldMap);
                        return;
                    }
                    CountDownLatch fieldCountDownLatch = new CountDownLatch(simpleFields.size());
                    simpleFields.stream().forEach(field -> {
                        fieldExecutor.execute(() -> {
                            try {
                                I18nField i18nField = I18nReflectUtils.getI18nFieldAnnotation(field);
                                String nameMappingInPlatform = i18nField.value();
                                if (StringUtils.isBlank(nameMappingInPlatform)) {
                                    log.error("I18nField 注解未设置value属性,业务Code：{}, ", businessCodeModel);
                                    return;
                                }
                                String substitution = fieldMap.get(nameMappingInPlatform);
                                log.debug("=====processSimpleField===>参数名字：{},========>值：{}", field.getName(),
                                        substitution);
                                if (StringUtils.isNotBlank(substitution)) {
                                    FieldUtils.writeField(field, model, substitution, true);
                                } else {
                                    log.debug("国际化值为空，跳过替换字段：{},原始值：{},替换值：{}", field.getName(), nameMappingInPlatform
                                            , substitution);
                                }
                            } catch (Exception e) {
                                log.error("processSimpleField field error", e);
                            } finally {
                                fieldCountDownLatch.countDown();
                            }
                        });
                    });
                    fieldCountDownLatch.await();
                } catch (Exception e) {
                    log.error("processSimpleField field error", e);
                } finally {
                    // 处理完成，state-1
                    modelCountDownLatch.countDown();
                }
            });
        });
        try {
            modelCountDownLatch.await();
        } catch (InterruptedException e) {
            log.error("processSimpleField modelCountDownLatch error", e);
        }
    }

    private List<String> extractBusinessKeyList(Collection<Object> models, String businessCodeModel,
                                                String businessKeyFieldNameModel) {
        List<String> businessKeyList = Lists.newArrayList();
        if (StringUtils.isNotEmpty(businessKeyFieldNameModel) && StringUtils.isNotEmpty(businessCodeModel)) {
            businessKeyList = models.stream().map(el -> {
                Object businessKeyOrg = null;
                try {
                    businessKeyOrg = FieldUtils.readField(el, businessKeyFieldNameModel, true);
                } catch (Exception e) {
                    log.error("read primary key error :", e);
                }
                if (businessKeyOrg != null) {
                    String businessKey = String.valueOf(businessKeyOrg);
                    return businessKey;
                } else {
                    return null;
                }
            }).filter(Objects::nonNull).collect(Collectors.toList());
        }
        return businessKeyList;
    }

    @Override
    public String name() {
        return null;
    }
}
